﻿using Core;
using MQTTnet;
using MQTTnet.Server;
using QMapper;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace BGYMqttServer.Handlers
{
    /// <summary>
    /// 表示订阅信息提供者
    /// </summary>
    public class MqttSubscriptionProvider : ISingletonDependency
    {
        private readonly IMqttServer mqttServer;
        private readonly Func<IMqttServer, MqttClientSessionsManager> sessionsManagerFunc;
        private readonly Func<MqttClientSessionsManager, ConcurrentDictionary<string, MqttClientSession>> sessionsFunc;
        private readonly Func<MqttClientSubscriptionsManager, Dictionary<string, TopicFilter>> subscriptionsFunc;

        /// <summary>
        /// 订阅信息提供者
        /// </summary>
        /// <param name="mqttServer"></param>
        public MqttSubscriptionProvider(IMqttServer mqttServer)
        {
            this.mqttServer = mqttServer;

            this.sessionsManagerFunc = CreateGetFunc<IMqttServer, MqttClientSessionsManager>(mqttServer);
            this.sessionsFunc = CreateGetFunc<MqttClientSessionsManager, ConcurrentDictionary<string, MqttClientSession>>();
            this.subscriptionsFunc = CreateGetFunc<MqttClientSubscriptionsManager, Dictionary<string, TopicFilter>>();
        }

        private static Func<T, TField> CreateGetFunc<T, TField>(T instance = default)
        {
            var type = instance?.GetType() ?? typeof(T);
            var field = type
                .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(item => item.FieldType == typeof(TField))
                .FirstOrDefault();

            return Lambda.CreateGetFunc<T, TField>(field);
        }

        /// <summary>
        /// 获取订阅信息
        /// </summary>
        /// <returns></returns>
        public MqttSubscription[] GetSubscriptions()
        {
            var sessionsManager = this.sessionsManagerFunc(this.mqttServer);
            var sessions = this.sessionsFunc(sessionsManager);

            return sessions.Select(item =>
            {
                var topicFilters = this.subscriptionsFunc(item.Value.SubscriptionsManager);
                return new MqttSubscription
                {
                    ClientId = item.Key,
                    TopicFilters = topicFilters.Select(i => i.Value.AsMap().To<TopicFilter>()).ToArray()
                };
            }).ToArray();
        }
    }
}
